#include <LNM8F01.h>
#include <intrins.h>
#include <stdio.h>
#include <absacc.h>
#include <string.h>
#include "system_init.h"
#include "beep_control.h"
#include "key_contorl.h"
#include "led_control.h"
#include "display_control.h"

#define FLASH_WRITE_OR_READ_SUCCESS      			0
#define FLASH_WRITE_OR_READ_FAIL         			1
#define FLASH_NONE_DATA          							2
#define SYSINFO_LENGTH												4
#define SYSINFO_ADDR                         	((uint16_t)0x8000)
#define FLASH_SECTOR_SIZE        							((uint16_t)0x200)

uint16_t infoOffset = 0;
// 系统时间计数变量
static uint32_t system_time_ms_g = 0;


void init_system(void)
{
    // 关闭看门狗
    WDTCON0 = 0x5A;

#ifdef PPL_24M
    // 初始化PLL，将系统时钟提升至24MHz
    init_ppl();
#endif
    // 初始化LVR
    init_lvr();
		
		//初始化flash
		init_flash();

    // 初始化定时器1
    init_timer1();
   
    // 初始化BEEP PWM 
    init_beep();
	
		//初始化按键
		init_key_control();
	
		//初始化显示
		init_display();
	
		//led初始化
		init_led_control();
		
		read_info();
}

//延时
void delay_block(uint16_t block)
{
    unsigned int i, j;
    for(i = 0; i < block; i++)
       for(j = 0; j < 50; j++);
}

//LVR初始化
void init_lvr(void)
{
    LVDCR |= 0x01 << 4; // LVR使用
}

//PPL初始化
void init_ppl(void)
{
    PLLCK &= ~(0x01 << 2);  // PLL使用晶振
    PLLCK |= 0x01;          // PLL使用
    
    // 3倍频率sysclk = 24M (8M * 3)
    PLLN = 1;
    PLLM = 3; 

    while((PLLCK&0x80) == 0);  // 等待PLL时钟稳定
    OSCCK |= 0x02;             // 系统时钟选择PLL
}

//FLASH初始化
void init_flash(void)
{
    // 使用HRC时钟(16M)
    // 系统时钟2分频
    TIMING = 24;
}

void unlock_flash(void)
{
		uint32_t currentTime = get_system_time();
    KEY = 0xA5;
    while(UNLOCK != 1)
		{
				if((get_system_time() - currentTime) >=10)
				{
					break;
				}		
		}
}

void lock_flash(void)
{
    KEY = 0xFF;
}

void program_flash(uint16_t addr, uint8_t dat)
{
		uint32_t currentTime = get_system_time();
    ARL = addr & 0xFF;
    ARH = addr >> 8;
    WDATA = dat;
    CMD = 0xA5;

    _nop_();
    _nop_();
    _nop_();
    _nop_();

    while(BUSY)
		{
				if((get_system_time() - currentTime) >=10)
				{
					break;
				}		
		}
}

void erase_flash_sector(uint16_t addr)
{
		uint32_t currentTime = get_system_time();
    ARL = addr & 0xFF;
    ARH = addr >> 8;
    CMD = 0x55;

    _nop_();
    _nop_();
    _nop_();
    _nop_();

    while (BUSY)
    {
				if((get_system_time() - currentTime) >=200)
				{
					break;
				}		
		}
}

void erase_flash(uint16_t addr, uint8_t cnt)
{
		uint8_t i=0;
		unlock_flash();
		for(i=0; i<cnt; i++)
		{
			erase_flash_sector(addr+i*FLASH_SECTOR_SIZE);
		}
		lock_flash();
}

void write_flash(uint8_t *pdat, uint16_t addr, uint8_t datLength)
{
		uint8_t i=0;
		unlock_flash();
		for(i=0; i<datLength; i++)
		{
			program_flash(addr+i, pdat[i]);
		}
		lock_flash();
}

void read_flash(uint8_t *pdat, uint16_t addr, uint8_t datLength)
{
		uint8_t i=0;
		unlock_flash();
		for(i=0; i<datLength; i++)
		{
			pdat[i] = CBYTE[addr+i];
		}
		lock_flash();
}

//写和校验信息
uint8_t write_flash_check(uint8_t *writeBuffer, uint32_t writeAddress, uint32_t writeLength, uint16_t offset)
{
	uint8_t i = 0, j = 0, trueCnt = 0;
	uint8_t rt_fg = FLASH_WRITE_OR_READ_FAIL;
	uint8_t backupsBuff[SYSINFO_LENGTH] = {0};
	if(writeLength >SYSINFO_LENGTH)
	{
		return FLASH_WRITE_OR_READ_FAIL;
	}
	writeAddress += offset *writeLength;
	
	for(i=0; i<5; i++)
	{
		write_flash(writeBuffer, writeAddress, writeLength);
		read_flash(backupsBuff, writeAddress, writeLength);
		for(j=0; j<SYSINFO_LENGTH; j++)
		{
			if(writeBuffer[i] != backupsBuff[i])
			{
				trueCnt = 0;
				break;
			}else
			{
				trueCnt++;
			}
		}
		if(trueCnt == SYSINFO_LENGTH)
		{
			rt_fg = FLASH_WRITE_OR_READ_SUCCESS;		
			break;
		}else
		{
			delay_block(100);
		}
	}
	return rt_fg;
}

//读和校验信息
uint8_t read_flash_check(uint8_t *readBuffer, uint32_t readAddress, uint32_t readLength, uint16_t offset)
{
	uint8_t checksum = 0;
	uint8_t i = 0, j = 0, j_cnt = 0;
	uint8_t rt_fg = FLASH_WRITE_OR_READ_FAIL;
   
	readAddress += offset *readLength;
	for(i=0; i<5; i++)
	{
		read_flash(readBuffer, readAddress, readLength);
		if(readBuffer[readLength-1] != 0xff)
		{
			rt_fg = FLASH_WRITE_OR_READ_SUCCESS;
			break;
		}else
		{
			j_cnt = 0;
			for(j=0; j<readLength; j++)
			{
				if(readBuffer[j] == 0xff)
				{
					j_cnt++;
				}                
			}
			if(j_cnt == readLength)
			{
				rt_fg = FLASH_NONE_DATA;
				break;
			}
		}
	}
	return rt_fg;

}

//读系统信息
void read_info(void)
{
	uint8_t r_buff[SYSINFO_LENGTH] = {0};
	uint16_t i = 0;
	uint16_t r_cnt = FLASH_SECTOR_SIZE/SYSINFO_LENGTH;
	infoOffset = 0;
	
	if(read_flash_check((uint8_t *)r_buff, SYSINFO_ADDR, SYSINFO_LENGTH, 0) != FLASH_NONE_DATA)  //判断地址首位置是否是为第一次使用
	{
		for(i=0; i<r_cnt; i++) 
		{
			memset(r_buff, 0xff, SYSINFO_LENGTH);
			if(read_flash_check((uint8_t *)r_buff, SYSINFO_ADDR, SYSINFO_LENGTH, i) == FLASH_WRITE_OR_READ_SUCCESS)
			{	
				infoOffset++;
				set_key_quantity(r_buff[0]);
				set_key_speed(r_buff[1]);
				set_key_mode(r_buff[2]);
				set_key_color(r_buff[3]);
			}else
			{
				break;
			}
		} 
	}
}

//写系统信息
void write_info(void)
{
	uint16_t r_cnt = FLASH_SECTOR_SIZE/SYSINFO_LENGTH;	
	static uint8_t pdat[SYSINFO_LENGTH] = {0};
	if(infoOffset >=r_cnt)
	{
		erase_flash(SYSINFO_ADDR, 1);
		infoOffset = 0;
	}
	pdat[0] = get_key_quantity();
	pdat[1] = get_key_speed();
	pdat[2] = get_key_mode();
	pdat[3] = get_key_color();
	if(write_flash_check((uint8_t*)pdat, SYSINFO_ADDR, SYSINFO_LENGTH, infoOffset) == FLASH_WRITE_OR_READ_SUCCESS)
	{
		infoOffset++;
	}
}


// 定时器1初始化  
void init_timer1(void)
{
#ifdef PPL_24M
    T1CNTH = 0xFE;
    T1CNTL = 0x89;
#else
		T1CNTH = 0xFF;
    T1CNTL = 0x83;
#endif
    TM1CK |= 0x08;      // Timer1时钟使用为系统时钟
    TM1CK |= 0x07;      // Timer1 128分频   
    T1CON |= 0x01;      // Timer1 使能
    IEN0 |= 0x80;       // 全中断允许
    IEN1 |= 0x01 << 2;  // Timer1中断使能
}

// 定时器1中断处理
void handle_timer1_irq(void) interrupt T1_IRQ
{
	T1CON &= ~(0x01 << 7);  // 清除中断标志
	system_time_ms_g++;     // 系统时间增加1
	// 在这里以固定的频率扫描数码管，避免主循环阻塞的影响
  scan_display();
}

// 获取系统时间（单位：毫秒）
uint32_t get_system_time(void)
{
    return system_time_ms_g;
}

// 毫秒级延时
void delay_ms(uint16_t ms)
{
    uint32_t startTime = get_system_time();
    while(get_system_time() - startTime < ms);
}



